package com.company.poker.client;

import com.company.poker.protocol.AddresseeType;
import com.company.poker.protocol.MessageType;
import com.company.poker.protocol.ProtocolType;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;


public class SimpleSocketClient implements Runnable, IClientContext {
    private final String host;
    private final int port;
    private Socket socket;
    private OutputStream writer;
    private SocketChannel channel;
    private BufferedReader reader;
    private List<IMessageHandler> handlers = new LinkedList<IMessageHandler>();

    public SimpleSocketClient(String host, int port) {
        this.port = port;
        this.host = host;
        try {
            socket = new Socket(host, port);
            socket.setKeepAlive(true);
            socket.setTcpNoDelay(true);
            writer = socket.getOutputStream();
            channel = socket.getChannel();
            reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void addHandler(IMessageHandler handler) {
        handlers.add(handler);
    }

    public void write(String msg) {
        int allocateSizeLength = 4;
        ByteBuffer byteHeader = createHeader();
        try {
            byte[] msgBody = msg.getBytes(Charset.forName("UTF-8"));
            byte[] headerArray = byteHeader.array();
            int size = msgBody.length + headerArray.length + allocateSizeLength;
            ByteBuffer byteBuffer = ByteBuffer.allocate(size);

            byteBuffer.putInt(0, size);
            int index = 4;
            for (int i=0; i< headerArray.length; i++) {
                byteBuffer.put(index, headerArray[i]);
                index ++;
            }
            for (int i=0; i< msgBody.length; i++) {
                byteBuffer.put(index, msgBody[i]);
                index ++;
            }
            writer.write(byteBuffer.array());
            writer.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     *
     <headerLength> - short 2 byte,
     <type> - Enum (1 byte, "0-127"),
     <form> - Enum (1 byte, "0-127"),
     <to>   - Enum (1 byte, "0-127"),
     <addressID> - String or Long (8 byte)  ,
     <messageID> - Enum (2 byte, short),
     <messageLength> - int, 4 byte
     <MESSAGE> - byte[]
     *
     *
     * @return
     */
    private ByteBuffer createHeader() {
        int allocatedSize = 49;
        String login = "rodin";
        String password = "secret";
        String curTime = String.valueOf(System.currentTimeMillis());
        ByteBuffer byteHeader = ByteBuffer.allocate(allocatedSize);
        byteHeader.put(0, (byte) ProtocolType.BINARY.getCode());
        byte[] tokenByte = UUID.nameUUIDFromBytes((login+password+curTime).getBytes()).toString().getBytes();
        int index = 0;
        index++;
        for (byte b : tokenByte) {
            byteHeader.put(index, b);
            index++;
        }
        byteHeader.put(index++, (byte) AddresseeType.USER.getCode());
        byteHeader.put(index++, (byte) AddresseeType.TABLE.getCode());
        byteHeader.putLong(index++,  Long.MAX_VALUE);
        byteHeader.putShort(47, (short) MessageType.PONG.getCode());
        return byteHeader;
    }

    public void run() {
        String msgInput;
        write("HELLO, WORLD");
        try {
            while (socket.isConnected() && (msgInput = reader.readLine()) != null) {
                System.out.println("Input: "+msgInput);
                for (IMessageHandler handler : handlers) {
                    handler.handle(this, msgInput);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    public void close() {
        try {
            handlers = null;
            writer.close();
            reader.close();
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
